package com.yumeng.generator;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
import com.baomidou.mybatisplus.generator.keywords.BaseKeyWordsHandler;
import com.baomidou.mybatisplus.generator.keywords.H2KeyWordsHandler;
import com.baomidou.mybatisplus.generator.keywords.MySqlKeyWordsHandler;
import com.baomidou.mybatisplus.generator.keywords.PostgreSqlKeyWordsHandler;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.apache.ibatis.type.JdbcType;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 * @author wxd
 * @date 2025/10/17 09:48
 */
@Slf4j
public abstract class AbstractGenerator {

    private final DbInfo dbInfo;

    public AbstractGenerator(DbInfo dbInfo){
        this.dbInfo = dbInfo;
    }

    /** 输出目录 */
    protected String getOutputDir(){
        return "C:/Users/wangxdon/Desktop/mybatis-test";
    }

    /** 作者 */
    protected String getAuthor(){
        return "wxd";
    }

    /** mapper包名 */
    protected String getMapperPkgName(){
        return "dao.mapper";
    }

    /** 是否启用别名 */
    protected boolean getEnableAlias(){
        return false;
    }

    /** 是否启用RequiresPermissions注解 */
    protected boolean getEnablePermissionsAnnotation(){
        return true;
    }

    /** 逻辑删除标志 */
    protected final String getLogicDelName(){
        return "delFlag";
    }

    /** 日期格式 */
    protected DateType getDateType(){
        return DateType.TIME_PACK;
    }

    /**
     * 执行生成
     *
     * @param genInfo
     */
    public void doGenerate(GenInfo genInfo){
        genInfo.checkUseful();
        FastAutoGenerator.create(dataSourceConfigBuilder()) // 数据库配置
                .globalConfig(this::globalConfigBuilder) // 全局配置
                .packageConfig(builder -> this.packageConfigBuilder(builder, genInfo)) // 包配置
                .strategyConfig(builder -> this.strategyConfigBuilder(builder, genInfo)) // 策略配置
                .injectionConfig(builder -> this.injectionConfigBuilder(builder, genInfo)) // 注入配置
                .templateEngine(new VelocityTemplateEngine()) //模板引擎配置
                .execute(); // 执行生成
    }

    //region builder

    /**
     * 数据库配置
     * @return
     */
    private DataSourceConfig.Builder dataSourceConfigBuilder(){
        BaseKeyWordsHandler wordsHandler = null;
        if (DbType.MYSQL.equals(this.dbInfo.getDbType())){
            wordsHandler = new MySqlKeyWordsHandler();
        } else if (DbType.POSTGRE_SQL.equals(this.dbInfo.getDbType())) {
            wordsHandler = new PostgreSqlKeyWordsHandler();
        } else if (DbType.H2.equals(this.dbInfo.getDbType())) {
            wordsHandler = new H2KeyWordsHandler();
        }
        DataSourceConfig.Builder builder = new DataSourceConfig.Builder(this.dbInfo.getDbUrl(), this.dbInfo.getDbUsername(), this.dbInfo.getDbPassword());
        if (wordsHandler != null){
            builder.keyWordsHandler(wordsHandler);
        }
        //builder.schema() //后续用到了再研究
        builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
            if (JdbcType.DOUBLE.equals(metaInfo.getJdbcType())) {
                return DbColumnType.DOUBLE;
            } else if (JdbcType.TINYINT == metaInfo.getJdbcType()) { // 兼容旧版本转换成Integer
                return DbColumnType.INTEGER;
            }
            return typeRegistry.getColumnType(metaInfo);
        });
        return builder;
    }

    /**
     * 全局配置
     * @param builder
     */
    private void globalConfigBuilder(GlobalConfig.Builder builder){
        builder.author(getAuthor()) // 设置作者
                //.enableKotlin() //开启 Kotlin 模式
                .enableSwagger() // 开启 swagger 模式
                .disableOpenDir() //禁止打开输出目录
                .dateType(getDateType()) //设置时间类型策略
                .commentDate("yyyy-MM-dd HH:mm:ss") //设置注释日期格式
                .outputDir(getOutputDir()); // 指定输出目录
    }

    /**
     * 包配置
     * @param builder
     */
    private void packageConfigBuilder(PackageConfig.Builder builder, GenInfo genInfo){
        builder.parent(genInfo.getParentPkgName()) // 设置父包名
                .moduleName(genInfo.getModuleName()) // 设置父包模块名
                //.entity("model") // 指定实体包名
                //.service("service") // 指定service接口包名
                //.serviceImpl("service.impl") // service实现类包名
                //.controller("controller") //指定控制器包名
                .mapper(getMapperPkgName()) //指定mapper接口包名
                .xml(getMapperPkgName()); //指定xml包名
                //.pathInfo(getPathInfo()); // 路径配置信息
                //.joinPackage(getPathInfo()); // 连接父子包名
    }

    /**
     * 策略配置
     * 指定模板路径，如果没有带模板后缀，则根据模板引擎动态赋值 如 .vm .btl .ftl
     * @param builder
     * @param genInfo
     */
    private void strategyConfigBuilder(StrategyConfig.Builder builder, GenInfo genInfo){
        builder
                //.disableSqlFilter()//禁用sql过滤
                //.enableSchema()//启用 schema
                .addInclude(genInfo.getTables()) // 设置需要生成的表名
                .addTablePrefix(ListUtils.emptyIfNull(genInfo.getTablePrefixes())) // 设置过滤表前缀
                //.outputFile()//输出文件处理
            .entityBuilder() // entity实体策略配置
                //.nameConvert() //名称转换实现
                .superClass(getEntitySuperClass(genInfo.getBaseEntityType()))//自定义继承的Entity类全称
                .disableSerialVersionUID() //禁用生成 serialVersionUID
                //.enableSerialAnnotation()
                //.enableColumnConstant()//开启生成字段常量
                .enableChainModel() //开启链式模型 	默认值:false
                .enableLombok() //开启 lombok 模型
                .enableTableFieldAnnotation() //开启生成实体时生成字段注解
                //.enableActiveRecord()//开启 ActiveRecord 模式
                .versionColumnName("version")//乐观锁字段名(数据库)
                .versionPropertyName("version")//乐观锁属性名(实体)
                //.logicDeleteColumnName("")//逻辑删除字段名(数据库)
                //.logicDeletePropertyName("")//逻辑删除属性名(实体)
                .naming(NamingStrategy.underline_to_camel)//数据库表映射到实体的命名策略
                .columnNaming(NamingStrategy.underline_to_camel)//数据库表字段映射到实体的命名策略 未指定按照 naming 执行
                .addSuperEntityColumns(getSuperEntityColumns(genInfo.getBaseEntityType()))//添加父类公共字段
                //.addIgnoreColumns()//添加忽略字段
                //.addTableFills(new Column("create_time", FieldFill.INSERT))//添加表字段填充 填充字段一般都在父类中
                //.addTableFills(new Column("create_time", FieldFill.INSERT))    //基于数据库字段填充
                //.addTableFills(new Property("updateTime", FieldFill.INSERT_UPDATE))    //基于模型属性填充
                .idType(IdType.ASSIGN_ID)//全局主键类型
                //.convertFileName()//转换输出文件名称
                //.formatFileName("%sEntity")//格式化文件名称
                .enableFileOverride()
                .javaTemplate("/templates/myEntity.java")//指定模板路径（entity）
            .controllerBuilder() // controller 策略配置
                .superClass("com.yumeng.framework.core.base.controller.BaseController")
                .enableRestStyle()
                //.convertFileName() //转换输出文件名称
                //.formatFileName() //格式化文件名称
                .enableFileOverride()
                .template("/templates/myController.java")//指定模板路径（controller）
            .mapperBuilder() // mapper 策略配置
                .superClass("com.yumeng.framework.persistence.mybatis.base.dao.MyBaseMapper")
                //.mapperAnnotation() //标记 Mapper 注解
                .enableBaseResultMap()
                .enableBaseColumnList()
                .cache(null) //设置缓存实现类
                .formatMapperFileName("%sMapper")
                .formatXmlFileName("%sMapper")
                .enableFileOverride()
                .mapperTemplate("/templates/myMapper.java")//指定模板路径（mapper）
                .mapperXmlTemplate("/templates/myMapper.xml")//指定模板路径（xml）
            .serviceBuilder()// service 策略配置
                .superServiceClass("com.yumeng.framework.core.base.service.BaseService")
                .superServiceImplClass("com.yumeng.framework.core.base.service.impl.BaseServiceImpl")
                .formatServiceFileName("%sService")//去掉默认文件名前的‘I’
                .enableFileOverride()
                .serviceTemplate("/templates/myService.java")//指定模板路径（service）
                .serviceImplTemplate("/templates/myServiceImpl.java");//指定模板路径（serviceImpl）
    }

    /**
     * 注入配置
     * @param builder
     * @param genInfo
     */
    private void injectionConfigBuilder(InjectionConfig.Builder builder, GenInfo genInfo){
        builder.beforeOutputFile((tableInfo, objectMap) -> {
                    //在生成文件之前执行自定义逻辑，如打印表信息或修改配置数据
                    checkEntitySuperClass(genInfo.getBaseEntityType(), tableInfo);
                    modifyObjectMap(tableInfo, objectMap);
                })
                //用于在模板中访问自定义的配置信息，如项目名称、作者等
                .customMap(getCustomMap())
                //用于指定自定义的模板文件路径，可以格式化文件名，参考测试用例 H2CodeGeneratorTest.testCustomFileByList
                .customFile(getCustomFiles());
    }

    //endregion

    //region private

    /**
     * 获取Entity对象的父类型
     * @param baseEntityType
     * @return
     */
    private String getEntitySuperClass(BaseEntityType baseEntityType){
        if (BaseEntityType.TEMP.equals(baseEntityType)){
            return "com.yumeng.framework.persistence.mybatis.base.entity.MpBaseEntity";
        }else if (BaseEntityType.CREATE.equals(baseEntityType)){
            return "com.yumeng.framework.persistence.mybatis.base.entity.MpBaseEntity_C";
        }else if (BaseEntityType.CREATE_DEL.equals(baseEntityType)){
            return "com.yumeng.framework.persistence.mybatis.base.entity.MpBaseEntity_CD";
        }else if (BaseEntityType.CREATE_UPDATE.equals(baseEntityType)){
            return "com.yumeng.framework.persistence.mybatis.base.entity.MpBaseEntity_CU";
        }else if (BaseEntityType.CREATE_UPDATE_DEL.equals(baseEntityType)){
            return "com.yumeng.framework.persistence.mybatis.base.entity.MpBaseEntity_CUD";
        }else{
            throw new GeneratorException("Entity父类型异常");
        }
    }

    /**
     * 添加父类公共字段
     * @param baseEntityType
     * @return
     */
    private List<String> getSuperEntityColumns(BaseEntityType baseEntityType){
        List<String> targetColumns;
        if (BaseEntityType.TEMP.equals(baseEntityType)){
            targetColumns = new ArrayList<>();
        }else if (BaseEntityType.CREATE.equals(baseEntityType)){
            targetColumns = Arrays.asList("creater", "create_time");
        }else if (BaseEntityType.CREATE_DEL.equals(baseEntityType)){
            targetColumns = Arrays.asList("creater", "create_time", "del_flag", "deler" , "del_time");
        }else if (BaseEntityType.CREATE_UPDATE.equals(baseEntityType)){
            targetColumns = Arrays.asList("creater", "create_time","updater", "update_time");
        }else if (BaseEntityType.CREATE_UPDATE_DEL.equals(baseEntityType)){
            targetColumns = Arrays.asList("creater", "create_time","updater", "update_time", "del_flag", "deler" , "del_time");
        }else{
            throw new GeneratorException("Entity父类型异常");
        }
        return targetColumns;
    }


    /**
     * 验证Entity对象的父类型的有效性
     * @param baseEntityType
     * @param tableInfo
     */
    private void checkEntitySuperClass(BaseEntityType baseEntityType, TableInfo tableInfo){
        List<String> targetColumns = getSuperEntityColumns(baseEntityType);
        List<TableField> commFields = tableInfo.getCommonFields();
        if (targetColumns.size() != commFields.size() || !commFields.stream().map(TableField :: getName).allMatch(targetColumns::contains)){
            throw new GeneratorException("Entity对象指定的父类型异常，%s-%s",tableInfo.getEntityName(), baseEntityType.name());
        }
    }

    /**
     * 修改map数据
     * @param tableInfo
     * @param objectMap
     */
    private void modifyObjectMap(@NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap){
        log.info("tableInfo: " + tableInfo.getEntityName() + " objectMap: " + objectMap.size());

        String entityName = tableInfo.getEntityName();
        String serviceName = tableInfo.getServiceName();
        String mapperName = tableInfo.getMapperName();

        String daoName = entityName + "Dao";
        objectMap.put("table_daoName", daoName);

        String lowerFirstEntityName = StringUtils.firstToLowerCase(entityName);
        objectMap.put("table_lowerFirstEntityName", lowerFirstEntityName);

        String lowerFirstServiceName = StringUtils.firstToLowerCase(serviceName);
        objectMap.put("table_lowerFirstServiceName", lowerFirstServiceName);

        String lowerFirstMapperName = StringUtils.firstToLowerCase(mapperName);
        objectMap.put("table_lowerFirstMapperName", lowerFirstMapperName);

        String lowerFirstDaoName = StringUtils.firstToLowerCase(daoName);
        objectMap.put("table_lowerFirstDaoName", lowerFirstDaoName);

        String tabComment = tableInfo.getComment();
        //方法操作对象名（用于api文档、日志记录）
        objectMap.put("funcObjName", StringUtils.isNotBlank(tabComment) ? String.format("%s（%s）", tabComment, entityName) : entityName);
        //方法注释对象名（用于方法注释）
        objectMap.put("funcCommentObjName", StringUtils.isNotBlank(tabComment) ? tabComment : entityName);
        //类注释对象名（用于类注释）
        objectMap.put("classCommentObjName", StringUtils.isNotBlank(tabComment) ? String.format("%s（%s）", tabComment, entityName) : entityName);

        String keyPropName = null;
        for (TableField field : tableInfo.getFields()){
            if (field.isKeyFlag()){
                keyPropName = field.getPropertyName();
                break;
            }
        }
        if (StringUtils.isBlank(keyPropName)){
            throw new GeneratorException("【%s】表主键缺失", tableInfo.getName());
        }
        objectMap.put("keyPropName", keyPropName);
        objectMap.put("firstUpperKeyPropName", CommUtils.firstToUpperCase(keyPropName));

        //判断属性，1、是否有逻辑删除字段 2、是否有日期字段
        String logicDelName = getLogicDelName();
        boolean logicDel = false;
        boolean hasLocalDateTimeFiled = false;
        for (TableField field : tableInfo.getCommonFields()){
            if (logicDelName.equals(field.getPropertyName())){
                logicDel = true;
            }
            if (field.getPropertyType().equals(LocalDateTime.class.getSimpleName())){
                hasLocalDateTimeFiled = true;
            }
        }
        objectMap.put("logicDel", logicDel);
        objectMap.put("hasLocalDateTimeFiled", hasLocalDateTimeFiled);

        //拼接表的列集合
        String fieldNames = tableInfo.getFieldNames();
        String commonFieldNames = tableInfo.getCommonFields().stream().map(TableField::getColumnName).collect(Collectors.joining(CommConstants.COMMA_FLAG));
        commonFieldNames = StringUtils.isNotBlank(commonFieldNames) ? CommConstants.COMMA_FLAG + commonFieldNames : "";
        objectMap .put("table_baseColumns", fieldNames + commonFieldNames);

        /**
         * 验证表信息名称是否合法
         */
        List<String> words = new ArrayList<>();
        words.add(tableInfo.getName());
        for (TableField field : tableInfo.getFields()){
            words.add(field.getName());
            words.add(field.getComment());
        }
        checkWords(tableInfo.getName(), words);
    }

    /**
     * 验证是否存在不合法描述
     * @param tableName
     * @param words
     */
    private void checkWords(String tableName, List<String> words){
        for (String word : words){
            if (StringUtils.isNotBlank(word)){
                if (word.contains("\n") || word.contains("\r")){
                    throw new GeneratorException("表（%s）存在不合法描述：%s", tableName, words.toString());
                }
            }
        }
    }

    /**
     * 获取自定义Map
     * @return
     */
    private Map<String, Object> getCustomMap(){
        Map<String, Object> map = new HashMap<>();
        map.put("alias", getEnableAlias());
        map.put("dtoExcludes", Arrays.asList("version", "isDel", "delFlag", "isActive", "activeFlag",
                "isStoped", "stopFlag", "stopedFlag", "stoper", "stopTime",
                "creater", "updater", "createTime", "updateTime", "state", "status"));
        map.put("voExcludes", Arrays.asList("version", "stoper", "stopTime",
                "creater", "updater", "createTime", "updateTime"));
        map.put("notBlankTypes", Arrays.asList("String", "LocalDateTime"));

        map.put("permAnnotation", getEnablePermissionsAnnotation());
        setCrudOptFlagName(map);
        return map;
    }

    /**
     * 自定义模板文件列表
     * @return
     */
    private List<CustomFile> getCustomFiles(){
        List<CustomFile> customFiles = new ArrayList<>();
        customFiles.add(new CustomFile.Builder().fileName("PageDto.java").templatePath("/templates/myEntityPageDto.java.vm").packageName("other/dto/page").enableFileOverride().build());
        customFiles.add(new CustomFile.Builder().fileName("OptDto.java").templatePath("/templates/myEntityOptDto.java.vm").packageName("other/dto/opt").enableFileOverride().build());

        customFiles.add(new CustomFile.Builder().fileName("InfoVo.java").templatePath("/templates/myEntityInfoVo.java.vm").packageName("other/vo/info").enableFileOverride().build());
        customFiles.add(new CustomFile.Builder().fileName("ListVo.java").templatePath("/templates/myEntityListVo.java.vm").packageName("other/vo/list").enableFileOverride().build());

        customFiles.add(new CustomFile.Builder().fileName("Dao.java").templatePath("/templates/myDao.java.vm").packageName("dao").enableFileOverride().build());
        customFiles.add(new CustomFile.Builder().fileName("Convert.java").templatePath("/templates/myConvert.java.vm").packageName("other/convert").enableFileOverride().build());
        return customFiles;
    }

    /**
     * 设置CRUD操作名称
     * @param objectMap
     */
    private void setCrudOptFlagName(Map<String, Object> objectMap){
        objectMap.put("addOptFlag", "save");//新增
        objectMap.put("addOptFlagPerm", "add");

        objectMap.put("updateOptFlag", "edit");//修改
        objectMap.put("updateOptFlagPerm", "update");

        objectMap.put("deleteOptFlag", "deleteOne");//真删除
        objectMap.put("deleteOptFlagPerm", "delete");
        objectMap.put("logicDelOptFlag", "removeOne");//逻辑删除
        objectMap.put("logicDelOptFlagPerm", "remove");

        objectMap.put("infoOptFlag", "getOne");//查看详情
        objectMap.put("infoOptFlagPerm", "info");

        objectMap.put("pageOptFlag", "page");//分页查看
        objectMap.put("pageOptFlagPerm", "page");
    }

    //endregion
}
